/*****************************************************************************************
This file is a part of Arise library.

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

Arise Engine		IScene2d.cpp

					E-mail:		arise_engine@mail.ru

created:			01.05.2008
last changes:		22.09.2008
*****************************************************************************************/
#include "stdafx.h"
#pragma warning(disable : 4800)		//	forcing value (performance warning)

#define	HEADER_BASE			"base"


IScene2d::IScene2d()
{
	m_render_device	= 0;
	m_file_system	= 0;
	m_settings		= 0;

	m_scale_factor_x	= 1.0f;
	m_scale_factor_y	= 1.0f;
}
IScene2d::~IScene2d()
{
}


Sprite* IScene2d::CreateSprite(	const std::string&	tex_name, 
								float				frame_x, 
								float				frame_y, 
								float				width, 
								float				height		)
{
	return CreateSprite(*m_texture_pool.add(tex_name), frame_x, frame_y, width, height);
}


Sprite* IScene2d::CreateSprite(	LPDIRECT3DTEXTURE9	tex, 
								float				frame_x, 
								float				frame_y, 
								float				width, 
								float				height		)
{
	Sprite* sprite = new Sprite(tex, frame_x, frame_y, width, height, m_scale_factor_x, m_scale_factor_y);
	sprite->SetDevice(m_render_device);
	sprite->SetPoolId(GetId());
	sprite->SetId((int)m_sprites.size());
	m_sprites.push_back(sprite);

	return sprite;
}


Sprite* IScene2d::CreateSprite(const SpriteHeader& sprite_header)
{
	Sprite* sprite = 0;

	if(!sprite_header.texture.empty())
	{
		LPDIRECT3DTEXTURE9*	tex = m_texture_pool.add(sprite_header.texture);
		if(tex)	sprite = new Sprite(*tex, sprite_header, m_scale_factor_x, m_scale_factor_y);
		else	sprite = new Sprite(0, sprite_header, m_scale_factor_x, m_scale_factor_y);
	}
	else
	{
		sprite = new Sprite(0, sprite_header, m_scale_factor_x, m_scale_factor_y);
	}

	sprite->SetDevice(m_render_device);
	sprite->SetPoolId(GetId());
	sprite->SetId((int)m_sprites.size());
	m_sprites.push_back(sprite);

	return sprite;
}


Sprite* IScene2d::CreateSprite(	const std::string&	template_name,
								ConfigTable*		config_table	)

{
	SpriteHeader header;
	if(!LoadSpriteHeader(header, template_name, config_table)) return 0;
	return CreateSprite(header);
}


const char FNTHEADERTAG[]="[HGEFONT]";
const char FNTBITMAPTAG[]="Bitmap";
const char FNTCHARTAG[]="Char";


char* _get_line(char *file, char *line)
{
	int i=0;

	while(file[i] && file[i]!='\n' && file[i]!='\r')
	{
		line[i]=file[i];
		i++;
	}
	line[i]=0;

	if(!file[i]) return 0;

	while(file[i] && (file[i]=='\n' || file[i]=='\r')) i++;

	return file+i;
}


Font* IScene2d::CreateFont(const std::string& font_name)
{
	if(!m_file_system) return 0;

	std::string full_path = font_name;
	if(m_settings) full_path = m_settings->file_path.font + font_name;

	FileData* file = m_file_system->GetFile(full_path);
	if(!file)
	{
		//	file not found or memory not enough
		return 0;
	}

	Font* font = new Font();
	m_fonts.push_back(font);

	void	*data;
	DWORD	size;
	char	*desc, *pdesc;
	char	linebuf[256];
	char	buf[MAX_PATH], *pbuf;
	char	chr;
	int		i=0, x, y, w, h, a, c;
	LPDIRECT3DTEXTURE9 tex;

	// Load font description

	data = file->GetData();
	size = file->GetSize();

	desc = new char[size+1];
	memcpy(desc,data,size);
	desc[size]=0;

	pdesc=_get_line(desc,linebuf);
	if(strcmp(linebuf, FNTHEADERTAG))
	{
		log(1, "Font %s has incorrect format", file->GetNameCStr());
		delete[] desc;	
		return 0;
	}

	// Parse font description

	while((pdesc=_get_line(pdesc,linebuf)))
	{
		if(!strncmp(linebuf, FNTBITMAPTAG, sizeof(FNTBITMAPTAG)-1 ))
		{
			pbuf=strrchr(buf,'\\');
			if(!pbuf) pbuf=strrchr(buf,'/');
			if(!pbuf) pbuf=buf;
			else pbuf++;
			if(!sscanf(linebuf, "Bitmap = %s", pbuf)) continue;

			if(m_settings)	tex = *m_texture_pool.add(pbuf, m_settings->file_path.font);
			else			tex = *m_texture_pool.add(pbuf);
		}
		else if(!strncmp(linebuf, FNTCHARTAG, sizeof(FNTCHARTAG)-1 ))
		{
			pbuf=strchr(linebuf,'=');
			if(!pbuf) continue;
			pbuf++;
			while(*pbuf==' ') pbuf++;
			if(*pbuf=='\"')
			{
				pbuf++;
				i=(unsigned char)*pbuf++;
				pbuf++; // skip "
			}
			else
			{
				i=0;
				while((*pbuf>='0' && *pbuf<='9') || (*pbuf>='A' && *pbuf<='F') || (*pbuf>='a' && *pbuf<='f'))
				{
					chr=*pbuf;
					if(chr >= 'a') chr-='a'-':';
					if(chr >= 'A') chr-='A'-':';
					chr-='0';
					if(chr>0xF) chr=0xF;
					i=(i << 4) | chr;
					pbuf++;
				}
				if(i<0 || i>255) continue;
			}
			sscanf(pbuf, " , %d , %d , %d , %d , %d , %d", &x, &y, &w, &h, &a, &c);

			font->letters[i] = CreateSprite(tex, (float)x, (float)y, (float)w, (float)h);
			font->pre[i]=(float)a;
			font->post[i]=(float)c;
			if(h>font->GetHeight()) font->SetHeight((float)h);
		}
	}

	delete[] desc;	
	SAFE_DELETE(file);	//	free memory

	return font;
}


Font* IScene2d::CreateFont(const FontHeader& font_header)
{
	Font* font = CreateFont(font_header.filename);
	if(!font) return 0;

	font->SetScale(font_header.scale);
	font->SetRotation(font_header.rotation);
	font->SetTracking(font_header.tracking);
	font->SetSpacing(font_header.spacing);
	font->SetColor(font_header.color);
	font->SetBlendMode(font_header.blend);

	return font;
}


Font* IScene2d::CreateFont(const std::string& template_name, ConfigTable* config_table)
{
	FontHeader header;
	if(!LoadFontHeader(header, template_name, config_table)) return 0;
	return CreateFont(header);
}


void IScene2d::Release()
{
	m_texture_pool.release();

	for(	std::vector<Sprite*>::iterator it = m_sprites.begin(); 
			it != m_sprites.end(); it++)
	{
			if((*it))
			{
				(*it)->Release();
				delete (*it);
			}
	}
	m_sprites.clear();

	for(	std::vector<Font*>::iterator it = m_fonts.begin(); 
			it != m_fonts.end(); it++)
	{
			if((*it))
			{
				delete (*it);
			}
	}
	m_sprites.clear();
}


//-------------------------------------------------------------------------------------//


bool IScene2d::LoadSpriteHeader(	SpriteHeader&		header,
									const std::string&	template_name, 
									ConfigTable*		config_table	)
{
	std::string	base = config_table->GetString(template_name, HEADER_BASE, "");
	SpriteHeader header_base;
	if(!base.empty())
	{
		LoadSpriteHeader(header_base, base, config_table);
	}

	header.name			= config_table->GetString(template_name,	SPRTHD_NAME,	template_name);
	header.texture		= config_table->GetString(template_name,	SPRTHD_TEXTURE, header_base.texture);
	header.rect.x1		= config_table->GetFloat(template_name,		SPRTHD_RECT,	header_base.rect.x1, 0);
	header.rect.y1		= config_table->GetFloat(template_name,		SPRTHD_RECT,	header_base.rect.y1, 1);
	header.rect.x2		= config_table->GetFloat(template_name,		SPRTHD_RECT,	header_base.rect.x2, 2);
	header.rect.y2		= config_table->GetFloat(template_name,		SPRTHD_RECT,	header_base.rect.y2, 3);
	header.hotspot[0]	= config_table->GetFloat(template_name,		SPRTHD_HOTSPOT,	header_base.hotspot[0], 0);
	header.hotspot[1]	= config_table->GetFloat(template_name,		SPRTHD_HOTSPOT,	header_base.hotspot[1], 1);
	header.frames		= config_table->GetInt(template_name,		SPRTHD_FRAMES,	header_base.frames);
	header.speed		= config_table->GetFloat(template_name,		SPRTHD_SPEED,	header_base.speed);
	header.play			= config_table->GetInt(template_name,		SPRTHD_PLAY,	header_base.play);
	
	UINT num_param = config_table->GetNumElemets(template_name, SPRTHD_COLOR);
	if(num_param && num_param<4)
	{
		log(1, "Sprite header '%s' (%s): incorrect number of color arguments (%d), 4 is expected (ARGB)", 
					template_name.c_str(), config_table->GetFilename().c_str(), num_param);
	}
	else
	{
		int a = config_table->GetInt(template_name,			SPRTHD_COLOR,	GETA(header_base.color), 0);
		int r = config_table->GetInt(template_name,			SPRTHD_COLOR,	GETR(header_base.color), 1);
		int g = config_table->GetInt(template_name,			SPRTHD_COLOR,	GETG(header_base.color), 2);
		int b = config_table->GetInt(template_name,			SPRTHD_COLOR,	GETB(header_base.color), 3);
		header.color = ARGB(a,r,g,b);
	}

	num_param = config_table->GetNumElemets(template_name, SPRTHD_BLEND);
	std::string param_str;
	int mode = 0;
	if(num_param>0)
	{
		for(UINT i = 0; i < num_param; i++)
		{
			param_str = config_table->GetString(template_name, SPRTHD_BLEND, "", i);
			if(param_str == SPRTHD_COLORADD)	mode |= BLEND_COLORADD;
			if(param_str == SPRTHD_COLORMUL)	mode |= BLEND_COLORMUL;
			if(param_str == SPRTHD_ALPHAADD)	mode |= BLEND_ALPHAADD;
		}
		header.blend = mode;
	}
	else header.blend = header_base.blend;

	header.position[0]	= config_table->GetFloat(template_name,		SPRTHD_POS,		header_base.position[0], 0);
	header.position[1]	= config_table->GetFloat(template_name,		SPRTHD_POS,		header_base.position[1], 1);
	if(header.position[0]<0 && m_settings) header.position[0] = m_settings->window_width + header.position[0];
	if(header.position[1]<0 && m_settings) header.position[1] = m_settings->window_height + header.position[1];
	
	
	num_param = config_table->GetNumElemets(template_name, SPRTHD_ANIM);
	mode = -1;
	for(UINT i = 0; i < num_param; i++)
	{
		param_str = config_table->GetString(template_name, SPRTHD_ANIM, "", i);
		if(param_str == SPRTHD_ANIM_FWD)	mode += Sprite::FWD;
		if(param_str == SPRTHD_ANIM_REV)	mode += Sprite::REV;
		if(param_str == SPRTHD_ANIM_PING)	mode += Sprite::PINGPONG;
		if(param_str == SPRTHD_ANIM_LOOP)	mode += Sprite::LOOP;
	}
	if(mode < 0)	header.animation = header_base.animation;
	else			header.animation = mode;

	return true;
}


bool IScene2d::LoadFontHeader(	FontHeader&			header, 
								const std::string&	template_name, 
								ConfigTable*		config_table		)
{
	std::string	base = config_table->GetString(template_name, HEADER_BASE, "");
	FontHeader header_base;
	if(!base.empty())
	{
		LoadFontHeader(header_base, base, config_table);
	}

	header.filename	= config_table->GetString(template_name,	FONTHD_NAME,		header_base.filename);
	header.scale	= config_table->GetFloat(template_name,		FONTHD_SCALE,		header_base.scale);
	header.rotation	= config_table->GetFloat(template_name,		FONTHD_ROTATION,	header_base.rotation);
	header.tracking	= config_table->GetFloat(template_name,		FONTHD_TRACKING,	header_base.tracking);
	header.spacing	= config_table->GetFloat(template_name,		FONTHD_SPACING,		header_base.scale);

	UINT num_param = config_table->GetNumElemets(template_name, SPRTHD_COLOR);
	if(num_param && num_param<4)
	{
		log(1, "Sprite header '%s' (%s): incorrect number of color arguments (%d), 4 is expected (ARGB)", 
					template_name.c_str(), config_table->GetFilename().c_str(), num_param);
	}
	else
	{
		int a = config_table->GetInt(template_name,			SPRTHD_COLOR,	GETA(header_base.color), 0);
		int r = config_table->GetInt(template_name,			SPRTHD_COLOR,	GETR(header_base.color), 1);
		int g = config_table->GetInt(template_name,			SPRTHD_COLOR,	GETG(header_base.color), 2);
		int b = config_table->GetInt(template_name,			SPRTHD_COLOR,	GETB(header_base.color), 3);
		header.color = ARGB(a,r,g,b);
	}

	num_param = config_table->GetNumElemets(template_name, SPRTHD_BLEND);
	std::string param_str;
	int mode = 0;
	if(num_param>0)
	{
		for(UINT i = 0; i < num_param; i++)
		{
			param_str = config_table->GetString(template_name, SPRTHD_BLEND, "", i);
			if(param_str == SPRTHD_COLORADD)	mode |= BLEND_COLORADD;
			if(param_str == SPRTHD_COLORMUL)	mode |= BLEND_COLORMUL;
			if(param_str == SPRTHD_ALPHAADD)	mode |= BLEND_ALPHAADD;
		}
		header.blend = mode;
	}
	else header.blend = header_base.blend;

	return true;
}


DWORD IScene2d::CreateColor(const std::string& template_name, const std::string& variable_name, ConfigTable* config_table)
{
	//	defaults set to 0
	int a = config_table->GetInt(template_name,	variable_name,	0, 0);
	int r = config_table->GetInt(template_name,	variable_name,	0, 1);
	int g = config_table->GetInt(template_name,	variable_name,	0, 2);
	int b = config_table->GetInt(template_name,	variable_name,	0, 3);
	
	return ARGB(a,r,g,b);
}




